<?php

namespace App\Console\Commands;

use App\Models\ContractSignHistory;
use App\Services\ContractSign\ContractSignManager;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Symfony\Component\Console\Command\Command as SymfonyCommand;

class FetchContractDocumentCommand extends Command
{
    protected $signature = 'contract:fetch-document 
        {apply_no? : Contract sign history apply_no} 
        {--force : Re-download even if contract_file_path already exists}';

    protected $description = 'Fetch and store the original contract document for a contract_sign_histories record.';

    public function handle(ContractSignManager $contractSignManager): int
    {
        $applyNo = $this->argument('apply_no');

        if ($applyNo) {
            return $this->processSingle($applyNo, $contractSignManager);
        }

        $query = ContractSignHistory::query()
            ->where('status', 'approved')
            ->whereNull('contract_file_path');

        $count = $query->count();
        if ($count === 0) {
            $this->info('No approved contract sign histories pending download.');
            return SymfonyCommand::SUCCESS;
        }

        $this->info(sprintf('Processing %d contract sign histories...', $count));

        $failures = 0;

        $query->orderByDesc('id')->chunkById(50, function ($histories) use (&$failures, $contractSignManager) {
            foreach ($histories as $history) {
                $result = $this->processSingle($history->apply_no, $contractSignManager);
                if ($result !== SymfonyCommand::SUCCESS) {
                    $failures++;
                }
            }
        });

        if ($failures > 0) {
            $this->error(sprintf('%d contract(s) failed to download.', $failures));
            return SymfonyCommand::FAILURE;
        }

        $this->info('All pending contracts processed successfully.');
        return SymfonyCommand::SUCCESS;
    }

    protected function processSingle(string $applyNo, ContractSignManager $contractSignManager): int
    {
        $history = ContractSignHistory::query()->where('apply_no', $applyNo)->first();
        if (! $history) {
            $this->error(sprintf('Contract sign history not found via apply_no = %s', $applyNo));
            return SymfonyCommand::FAILURE;
        }

        if ($history->status !== 'approved') {
            $this->warn(sprintf('History #%d status is %s, skip fetching.', $history->id, $history->status));
            return SymfonyCommand::FAILURE;
        }

        if ($history->contract_file_path && ! $this->option('force')) {
            $this->info(sprintf(
                'History #%d already has stored file at [%s]. Use --force to re-download.',
                $history->id,
                $history->contract_file_path
            ));
            return SymfonyCommand::SUCCESS;
        }

        $diskName = config('filesystems.default', 's3');

        try {
            $disk = Storage::disk($diskName);
        } catch (\Throwable $e) {
            $this->error(sprintf('Unable to resolve storage disk [%s]: %s', $diskName, $e->getMessage()));
            return SymfonyCommand::FAILURE;
        }

        $result = $contractSignManager->driver()->fetchContractDocument($history->apply_no);
        if (! $result->success) {
            $this->error(sprintf('Failed to fetch contract download url: %s', $result->message));
            return SymfonyCommand::FAILURE;
        }

        $downloadUrl = $result->data['downloadUrl'] ?? null;
        if (! $downloadUrl) {
            $this->error('Driver response missing downloadUrl.');
            return SymfonyCommand::FAILURE;
        }

        $this->info(sprintf('Downloading contract from [%s]', $downloadUrl));

        try {
            $response = Http::timeout(60)->get($downloadUrl);
        } catch (\Throwable $e) {
            $this->error(sprintf('Download request failed: %s', $e->getMessage()));
            return SymfonyCommand::FAILURE;
        }

        if (! $response->successful()) {
            $this->error(sprintf('Download failed with status %s', $response->status()));
            return SymfonyCommand::FAILURE;
        }

        $content = $response->body();

        $targetPath = $this->resolveTargetPath($history, $downloadUrl);

        try {
            $disk->put($targetPath, $content);
        } catch (\Throwable $e) {
            $this->error(sprintf('Failed to store file on disk [%s]: %s', $diskName, $e->getMessage()));
            return SymfonyCommand::FAILURE;
        }

        $history->contract_download_url = $downloadUrl;
        $history->contract_file_path = $targetPath;
        $history->save();

        $this->info(sprintf('Contract stored to [%s] on disk [%s]', $targetPath, $diskName));

        return SymfonyCommand::SUCCESS;
    }

    protected function resolveTargetPath(ContractSignHistory $history, ?string $downloadUrl): string
    {
        $extension = $this->guessExtensionFromUrl($downloadUrl);

        $path = "contracts/{$history->task_id}/{$history->freelancer_id}_{$history->apply_no}.{$extension}";

        return $path;
    }

    protected function guessExtensionFromUrl(?string $downloadUrl): string
    {
        if (! $downloadUrl) {
            return 'pdf';
        }

        $path = parse_url($downloadUrl, PHP_URL_PATH);
        if (! $path) {
            return 'pdf';
        }

        $extension = pathinfo($path, PATHINFO_EXTENSION);

        if (! $extension) {
            return 'pdf';
        }

        $extension = strtolower($extension);

        if (strlen($extension) > 5) {
            return 'pdf';
        }

        return preg_replace('/[^a-z0-9]/', '', $extension) ?: 'pdf';
    }
}
